package search.service.search;

import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;
import search.config.AnalyzerNames;
import common.config.IndexTypeNames;
import search.config.SearchParameters;
import common.dao.elastic.ElasticSearchDao;
import common.helper.json.JsonFormatter;
import search.service.analysis.AnalysisService;
import search.service.analysis.EncodeDecode;
import search.service.analysis.QueryExpansion;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Component
public class SimpleSearchService_V2 {
    final Logger logger = LoggerFactory.getLogger(SimpleSearchService_V2.class);

    private final ElasticSearchDao elasticsearchDao;

    @Inject
    public SimpleSearchService_V2(ElasticSearchDao elasticsearchDao ) {
        this.elasticsearchDao = elasticsearchDao;
    }

	/************************************************************
	 *
	 * @param q
	 * @param from
	 * @param size
	 * @param explain
	 * @param mediatype
	 * @param sorttype
     * @return
     */
	public String simpleSearch(String q,  Integer from, Integer size, String mediatype, String sorttype, String minimum, String explain ,String dsl, String level) {
		/*
		 * query
		 */
		QueryBuilder query;
		// If the user does not provide any text to query, let's match all documents
		if (!Strings.hasText(q)) {
			query = QueryBuilders.matchAllQuery();
		} 
		else {
			BoolQueryBuilder multi = QueryBuilders.boolQuery();
			BoolQueryBuilder must_query = QueryBuilders.boolQuery();
			BoolQueryBuilder should_query = QueryBuilders.boolQuery();
			//String base_analyzer = AnalyzerNames.default_query_analyzer;
			/*
			ik_smart的扩展未必是ik_max_word的子集,
			比如"上海空境"	
			*/
			String must_analyzer = AnalyzerNames.ik_max_word;
			String base_analyzer = AnalyzerNames.ik_smart;
			String expa_analyzer = AnalyzerNames.ik_smart;
			//NOTE: 语义扩展后的每个词语都必须匹配
			String expa_minimum = "100%";
			String field = "_all";
			List<String> tokenList = AnalysisService.getTokenTermList(this.elasticsearchDao, q, must_analyzer);
			int tokenLength = tokenList.size();
			//minimum = "0";
			//BoolQueryBuilder base_query = QueryExpansion.queryWithSimpleStringQuery(q,field,minimum,base_analyzer);
			//BoolQueryBuilder base_query = QueryExpansion.queryWithMatchQuery(q, field, minimum, base_analyzer);
			// 0) ik_max_word
			// must
			BoolQueryBuilder smrt_query = QueryExpansion.queryWithMatchQuery(q, field, minimum, must_analyzer);
			if(tokenLength <= 1)
				must_query.should(
						smrt_query.boost(SearchParameters.Boosting.original_base_query)
				);
			else
				must_query.must(
						smrt_query.boost(SearchParameters.Boosting.original_base_query)
				);



			// 2) ik_smart_syno
			// should
			BoolQueryBuilder syno_query = QueryExpansion.queryExpansionWithSynoWithMatchQuery(this.elasticsearchDao, q, field,
					expa_minimum,expa_analyzer);
			must_query.should(
					syno_query.boost(SearchParameters.Boosting.original_syno_query)
			);

			// 3) expansion
			// using analyzers to expansion query...
			BoolQueryBuilder expansion_query = QueryBuilders.boolQuery();
			try{
				expansion_query = QueryExpansion.queryExpansionWithDictForMatchQuery(this.elasticsearchDao, q,field,
						expa_minimum,expa_analyzer);
			}catch (Exception e){
				e.printStackTrace();
			}
			should_query.should(
					expansion_query.boost(SearchParameters.Boosting.expasion_query)
			);

			// 4) field_query
			BoolQueryBuilder fields_query = QueryExpansion.queryWithFieldsMatchQuery(q,base_analyzer);
			should_query.should(
					fields_query
			);

			multi.must( must_query );
			multi.should( should_query );


			/*
			最外层query受内层sub_query设置的影响，
			minimum在不明确设置的情况下是 100%,
			需要明确设置为 0，
			只需匹配must子查询即可
			 */
			// DELETE: only need to match must_sub_query, no need for should_sub_match
			// only need to match one sub query
			multi.minimumShouldMatch("1");
			
			query = multi;
		}
		// only need to match must_sub_query, no need for should_sub_match
		// query.minimumShouldMatch("0");


		/*
		 * filter
		 * mediatype
		 */
		//FilterBuilder filter = null;
		AndQueryBuilder filter = QueryBuilders.andQuery();
		if(mediatype!=null && !mediatype.isEmpty()){
			mediatype = SearchParameters.MediaTypes.getMeidiaType(mediatype);
		}
		else{
			mediatype = SearchParameters.MediaTypes.video_type;
		}

		/*
        visible
         */
		boolean deal_visible = true;
		if(deal_visible){
			TermQueryBuilder baseFilter = QueryBuilders.termQuery("Metadata.Visible", 0);
			NotQueryBuilder notVisibleFilter = QueryBuilders.notQuery(baseFilter);
			filter.add(notVisibleFilter);
		}
		
		/*
		 * filteredQuery
		 */
		FilteredQueryBuilder filteredQuery = QueryBuilders.filteredQuery(
				query,
				filter
				);
		

		/*
        * <sorttype>
         */
		if(sorttype!=null && !sorttype.isEmpty()){
			sorttype = SearchParameters.SortTypes.getSortType(sorttype);
		}
		else{
			sorttype = SearchParameters.SortTypes.relevancy;
		}
		String sort_field = SearchParameters.SortTypes.sort_fields.get(sorttype);
		SortOrder sort_order = SearchParameters.SortTypes.sort_orders.get(sorttype);
		SortBuilder sort = new FieldSortBuilder(sort_field).order(sort_order).ignoreUnmapped(true);

		/*
		* levels
		 */
		List<String> levels = IndexTypeNames.MediaDB.getTableTypeList();
		if(!level.equals("")){
			levels = new ArrayList<>();
			levels.add(level);
		}


		/*
		* explain
		 */
		Boolean is_explain = EncodeDecode.is_explain(explain);

		/*
		* dsl
		 */
		Boolean is_dsl = EncodeDecode.is_dsl(dsl);
		if(is_dsl == true){
			SearchRequestBuilder request = this.elasticsearchDao.genSearchRequset(filteredQuery, from, size, sort, levels , is_explain);
			return request.toString();
		}



		/*
		 * search 
		 */
		SearchResponse response = elasticsearchDao.search(filteredQuery, from, size, sort, levels, is_explain);

		if (logger.isDebugEnabled()) logger.debug("search({})={} hits", q, response.getHits().getTotalHits());
		//return response.toString();
		return JsonFormatter.formatSearchResponse(response);
	}

	public static void main(String[] args){
		SimpleSearchService_V2 service = new SimpleSearchService_V2(new ElasticSearchDao("debug"));
		String result = service.simpleSearch("回", 0, 10 ,"video", "latest", "100%","false", "false", "program");
		System.out.println(result);
	}

}
